package cn.isekai.keycloak.avatar.utils;

import org.jboss.logging.Logger;

import javax.imageio.ImageIO;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;

/**
 * @author dracowyn
 * 原生 Java 图像处理工具类，替代 thumbnailator
 */
public class ImageUtils {
    private static final Logger logger = Logger.getLogger(ImageUtils.class);

    /**
     * 裁剪图片
     *
     * @param sourceImage 源图片
     * @param x           裁剪起始 x 坐标
     * @param y           裁剪起始 y 坐标
     * @param size        裁剪尺寸（正方形）
     * @return 裁剪后的图片
     */
    public static BufferedImage cropImage(BufferedImage sourceImage, int x, int y, int size) {
        // 确保裁剪区域在图片范围内
        int actualX = Math.max(0, Math.min(x, sourceImage.getWidth() - 1));
        int actualY = Math.max(0, Math.min(y, sourceImage.getHeight() - 1));
        int actualSize = Math.min(size, Math.min(sourceImage.getWidth() - actualX, sourceImage.getHeight() - actualY));

        // 如果裁剪区域就是整个图片，直接返回原图
        if (actualX == 0 && actualY == 0 && actualSize == sourceImage.getWidth() && actualSize == sourceImage.getHeight()) {
            return sourceImage;
        }

        // 使用 getSubimage 进行裁剪
        BufferedImage croppedImage = sourceImage.getSubimage(actualX, actualY, actualSize, actualSize);

        // 创建新的 BufferedImage 以确保数据完整性
        BufferedImage result = new BufferedImage(actualSize, actualSize, BufferedImage.TYPE_INT_ARGB);
        Graphics2D g2d = result.createGraphics();

        // 设置高质量渲染提示（虽然是1:1复制，但保持一致性）
        setHighQualityRenderingHints(g2d);

        g2d.drawImage(croppedImage, 0, 0, null);
        g2d.dispose();

        return result;
    }

    /**
     * 调整图片尺寸
     *
     * @param sourceImage  源图片
     * @param targetWidth  目标宽度
     * @param targetHeight 目标高度
     * @return 调整尺寸后的图片
     */
    public static BufferedImage resizeImage(BufferedImage sourceImage, int targetWidth, int targetHeight) {
        // 如果尺寸相同，直接返回原图
        if (sourceImage.getWidth() == targetWidth && sourceImage.getHeight() == targetHeight) {
            return sourceImage;
        }

        // 使用渐进式缩放以获得更好的质量
        BufferedImage currentImage = sourceImage;
        int currentWidth = sourceImage.getWidth();
        int currentHeight = sourceImage.getHeight();

        // 如果缩放比例超过50%，使用渐进式缩放
        double scaleRatio = Math.min((double) targetWidth / currentWidth, (double) targetHeight / currentHeight);

        if (scaleRatio < 0.5) {
            // 渐进式缩放：每次缩放不超过50%
            while (currentWidth > targetWidth * 2 || currentHeight > targetHeight * 2) {
                currentWidth = Math.max(currentWidth / 2, targetWidth);
                currentHeight = Math.max(currentHeight / 2, targetHeight);

                BufferedImage tempImage = new BufferedImage(currentWidth, currentHeight, BufferedImage.TYPE_INT_ARGB);
                Graphics2D g2d = tempImage.createGraphics();

                // 设置最高质量渲染提示
                setHighQualityRenderingHints(g2d);

                g2d.drawImage(currentImage, 0, 0, currentWidth, currentHeight, null);
                g2d.dispose();

                currentImage = tempImage;
            }
        }

        // 最终缩放到目标尺寸
        BufferedImage finalImage = new BufferedImage(targetWidth, targetHeight, BufferedImage.TYPE_INT_ARGB);
        Graphics2D g2d = finalImage.createGraphics();

        // 设置最高质量渲染提示
        setHighQualityRenderingHints(g2d);

        g2d.drawImage(currentImage, 0, 0, targetWidth, targetHeight, null);
        g2d.dispose();

        return finalImage;
    }

    /**
     * 设置高质量渲染提示
     *
     * @param g2d Graphics2D 对象
     */
    private static void setHighQualityRenderingHints(Graphics2D g2d) {
        // 使用最高质量的插值算法
        g2d.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BICUBIC);
        // 设置高质量渲染
        g2d.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY);
        // 开启抗锯齿
        g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
        // 设置文本抗锯齿
        g2d.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_ON);
        // 设置颜色渲染质量
        g2d.setRenderingHint(RenderingHints.KEY_COLOR_RENDERING, RenderingHints.VALUE_COLOR_RENDER_QUALITY);
        // 设置透明度插值质量
        g2d.setRenderingHint(RenderingHints.KEY_ALPHA_INTERPOLATION, RenderingHints.VALUE_ALPHA_INTERPOLATION_QUALITY);
        // 设置抖动
        g2d.setRenderingHint(RenderingHints.KEY_DITHERING, RenderingHints.VALUE_DITHER_ENABLE);
        // 设置分数字体度量
        g2d.setRenderingHint(RenderingHints.KEY_FRACTIONALMETRICS, RenderingHints.VALUE_FRACTIONALMETRICS_ON);
        // 设置描边控制
        g2d.setRenderingHint(RenderingHints.KEY_STROKE_CONTROL, RenderingHints.VALUE_STROKE_PURE);
    }

    /**
     * 将 BufferedImage 转换为 InputStream
     *
     * @param image  图片对象
     * @param format 输出格式（如 "PNG", "JPEG"）
     * @return InputStream 流
     * @throws IOException IO 异常
     */
    public static InputStream bufferedImageToInputStream(BufferedImage image, String format) throws IOException {
        ByteArrayOutputStream baos = new ByteArrayOutputStream();

        // 对于 PNG 格式，确保使用无损压缩
        if ("PNG".equalsIgnoreCase(format)) {
            // PNG 是无损格式，直接写入即可
            ImageIO.write(image, "PNG", baos);
        } else if ("JPEG".equalsIgnoreCase(format) || "JPG".equalsIgnoreCase(format)) {
            // 对于 JPEG，可以设置质量参数（这里暂时使用默认设置）
            // 如果需要高质量 JPEG，可以使用 ImageWriter 设置压缩质量
            ImageIO.write(image, "JPEG", baos);
        } else {
            // 默认使用 PNG 格式
            ImageIO.write(image, "PNG", baos);
        }

        return new ByteArrayInputStream(baos.toByteArray());
    }

    /**
     * 从 InputStream 读取图片
     *
     * @param inputStream 输入流
     * @return BufferedImage 图片对象
     * @throws IOException IO 异常
     */
    public static BufferedImage inputStreamToBufferedImage(InputStream inputStream) throws IOException {
        return ImageIO.read(inputStream);
    }

    /**
     * 处理头像图片：裁剪和生成多尺寸
     *
     * @param inputStream 原始图片输入流
     * @param cropX       裁剪 x 坐标（可选，传 -1 表示不裁剪）
     * @param cropY       裁剪 y 坐标（可选，传 -1 表示不裁剪）
     * @param cropSize    裁剪尺寸（可选，传 -1 表示不裁剪）
     * @param targetSize  目标尺寸
     * @return 处理后的图片输入流
     * @throws IOException IO 异常
     */
    public static InputStream processAvatar(InputStream inputStream, int cropX, int cropY, int cropSize, int targetSize) throws IOException {
        BufferedImage sourceImage = inputStreamToBufferedImage(inputStream);

        // 如果需要裁剪
        BufferedImage processedImage = sourceImage;
        if (cropX >= 0 && cropY >= 0 && cropSize > 0) {
            processedImage = cropImage(sourceImage, cropX, cropY, cropSize);
        }

        // 调整到目标尺寸
        if (processedImage.getWidth() != targetSize || processedImage.getHeight() != targetSize) {
            processedImage = resizeImage(processedImage, targetSize, targetSize);
        }

        return bufferedImageToInputStream(processedImage, "PNG");
    }

    /**
     * 验证图片格式
     *
     * @param inputStream 输入流
     * @return 是否为有效的图片格式
     */
    public static boolean isValidImage(InputStream inputStream) {
        try {
            BufferedImage image = ImageIO.read(inputStream);
            return image != null;
        } catch (IOException e) {
            logger.warn("Invalid image format: " + e.getMessage());
            return false;
        }
    }

    /**
     * 高质量 JPEG 输出（可选方法）
     *
     * @param image   图片对象
     * @param quality 质量参数 (0.0f - 1.0f，1.0f 为最高质量)
     * @return InputStream 流
     * @throws IOException IO 异常
     */
    public static InputStream bufferedImageToHighQualityJpeg(BufferedImage image, float quality) throws IOException {
        ByteArrayOutputStream baos = new ByteArrayOutputStream();

        javax.imageio.ImageWriter writer = ImageIO.getImageWritersByFormatName("JPEG").next();
        javax.imageio.ImageWriteParam param = writer.getDefaultWriteParam();

        if (param.canWriteCompressed()) {
            param.setCompressionMode(javax.imageio.ImageWriteParam.MODE_EXPLICIT);
            // 设置质量
            param.setCompressionQuality(quality);
        }

        javax.imageio.stream.ImageOutputStream ios = ImageIO.createImageOutputStream(baos);
        writer.setOutput(ios);
        writer.write(null, new javax.imageio.IIOImage(image, null, null), param);

        writer.dispose();
        ios.close();

        return new ByteArrayInputStream(baos.toByteArray());
    }
}
